/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 idle_processor.cpp

	$Header: /heroes4/idle_processor.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "idle_processor.h"

#include <algorithm>
#include <cassert>
#include <list>

#include "game_time.h"
/////////////////////////////////////////////////////////////////////////////
// Unnamed namespace


namespace
{
	/////////////////////////////////////////////////////////////////////////////
	t_idle_list_ptr get_active_idle_processor_ptrs()
	{
		static t_idle_list_ptr active_idle_processor_ptrs = new t_idle_list;

		return active_idle_processor_ptrs;
	}

	t_idle_list_ptr get_running_processors()
	{
		static t_idle_list_ptr running_list = new t_idle_list;

		return running_list;
	}

} // Unnamed namespace

/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
t_uint32 t_idle_processor::run()
{
	t_idle_list& idlers = *get_active_idle_processor_ptrs();

	if ( idlers.empty())
	{
		// We have nothing to do, but we might soon, so how about we just 
		// offer to sleep a little bit?
		return 10;
	}

	t_uint32          new_time = get_time();
	t_idle_processor* idler = idlers.front();
	int				  delay;

	delay = elapsed_time( idler->get_next_time(), new_time );
	if (delay > 0)
	{
		delay -= 5;
		if (delay < 0)
			delay = 0;

		// Return how long we'd like to sleep before having another go
		return delay;
	}

	t_idle_list&		  running = *get_running_processors();
	t_idle_list::iterator index;

	running.push_back( idler );
	idler->set_next_time();
	idler->on_idle();

	index = std::find( running.begin(), running.end(), idler );
	if (index != running.end())
	{
		new_time = get_time();
		if (idler->get_next_time() < new_time)
			idler->set_next_time( new_time );
		running.erase( index );
	}

	// We'd be happy to have another chance to run right away
	return 0;
}

/////////////////////////////////////////////////////////////////////////////
t_idle_processor::t_idle_processor( t_uint32 suspension_count, t_uint32 delay )
	:	m_suspension_count( suspension_count )
{
	m_list = get_active_idle_processor_ptrs();
	m_running_list = get_running_processors();
	m_delay = delay;

	//delay must be above 20 to prevent any one process to hog the processor time.
	//assert(m_delay >= 20);

	if (m_delay < 20) 
		m_delay = 20;

	m_next_update_time = get_time() + m_delay;
	insert();
}

/////////////////////////////////////////////////////////////////////////////
t_idle_processor::t_idle_processor( t_idle_processor const & other )
	:	m_suspension_count( other.m_suspension_count )
{
	m_list = get_active_idle_processor_ptrs();
	m_delay = other.m_delay;
	m_next_update_time = get_time() + m_delay;
	insert();
}

/////////////////////////////////////////////////////////////////////////////
t_idle_processor::~t_idle_processor()
{
	remove();
	m_running_list->remove( this );
}

/////////////////////////////////////////////////////////////////////////////
// insert into list
/////////////////////////////////////////////////////////////////////////////
void t_idle_processor::insert()
{
	t_idle_list& idlers = *m_list;

	if (m_list_position._Mynode() != NULL)
		return;
	if (m_suspension_count > 0)
		return;

	m_list_position = idlers.end();
	while (m_list_position != idlers.begin())
	{
		m_list_position--;
		if (elapsed_time( (*m_list_position)->get_next_time(), m_next_update_time) <= 0)
		{
			m_list_position++;
			break;
		}
	}
	m_list_position = idlers.insert( m_list_position, this );
}

/////////////////////////////////////////////////////////////////////////////
// remove from list
/////////////////////////////////////////////////////////////////////////////
void t_idle_processor::remove()
{
	if (m_list_position._Mynode() == NULL)
		return;

	t_idle_list& idlers = *m_list;

	idlers.erase( m_list_position );
	m_list_position = t_idle_iterator();
}

/////////////////////////////////////////////////////////////////////////////
// set next update time
/////////////////////////////////////////////////////////////////////////////
void t_idle_processor::set_next_time( t_uint32 arg )
{
	remove();
	m_next_update_time = arg;
	insert();
}

/////////////////////////////////////////////////////////////////////////////
void t_idle_processor::resume_idle_processing()
{
	assert( m_suspension_count > 0 );

	if ( --m_suspension_count == 0 )
	{
		m_next_update_time = get_time() + m_delay;
		insert();
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_idle_processor::suspend_idle_processing()
{
	if ( m_suspension_count++ == 0 )
		remove();
}




/////////////////////////////////////////////////////////////////////////////
t_idle_processor_no_delay::t_idle_processor_no_delay( t_uint32 suspension_count) : t_idle_processor(suspension_count,0)
{
	m_delay = 0;
	m_next_update_time = get_time() + m_delay;
}

/////////////////////////////////////////////////////////////////////////////
t_idle_processor_no_delay::t_idle_processor_no_delay( t_idle_processor const & other ): t_idle_processor(other)
{}

